interface IAttr {
  fillStyle: string;
  font: string;
  globalAlpha: number;
  lineCap: string;
  lineWidth: number;
  lineJoin: string;
  miterLimit: number;
  shadowBlur: number;
  shadowColor: string;
  strokeStyle: string;
  textAlign: string;
  textBaseline: string;
  lineDash: number[];
}

interface IBoundry {
  top: number;
  right: number;
  bottom: number;
  left: number;
}

interface IProp {
  id: number;
  zIndex: number;
  visible: boolean;
  destroyed: boolean;
  parent: IElement,
  context: CanvasRenderingContext2D;
}

interface IElement {
  attrs: Partial<IAttr>;
  props: Partial<IProp>;

  getProps(): Partial<IProp>;
  getProps<T extends keyof IProp>(name: T): IProp[T];

  setProps(name: Partial<IProp>): void;
  setProps<T extends keyof IProp>(name: T, value: IProp[T]): void;

  getAttrs(): Partial<IAttr>;
  getAttrs<T extends keyof IAttr>(name: T): IAttr[T];

  setAttrs(name: Partial<IAttr>): void;
  setAttrs<T extends keyof IAttr>(name: T, value: IAttr[T]): void;

  getParent(): IElement;

  getContext(): CanvasRenderingContext2D;

  getBoundry(): IBoundry;

  saveContext():void;

  setContext(ctx: CanvasRenderingContext2D): void;

  restoreContext():void;

  drawInner():void;

  createPath():void;

}

export interface IConfig extends Partial<IProp> {
  attrs?: Partial<IAttr>
}

export class Element implements IElement {

  public attrs: Partial<IAttr> = {};
  public props: Partial<IProp> = {
    id: null,
    zIndex: 0,
    parent: null,
    visible: true,
    destroyed: false,
    context: null,
  };

  constructor(config: IConfig = {}) {
    const { attrs, ...props } = config;
    if (attrs) this.attrs = Object.assign({}, this.attrs, attrs);
    this.props = Object.assign({}, this.props, props)
  }

  getProps(): Partial<IProp>
  getProps<T extends keyof IProp>(name: T): IProp[T]
  getProps<T extends keyof IProp>(name?: T) {
    if (name) {
      return this.props[name];
    }
    return this.props;
  }

  setProps(name: Partial<IProp>): void;
  setProps<T extends keyof IProp>(name: T, value: IProp[T]): void;
  setProps<T extends keyof IProp>(name: T, value?: IProp[T]): void {
    if (typeof name === 'string') {
      this.props[name] = value;
    }
    if (typeof name === 'object') {
      this.props = Object.assign({}, this.props, name);
    }
  }

  getAttrs(): Partial<IAttr>;
  getAttrs<T extends keyof IAttr>(name: T): IAttr[T];
  getAttrs<T extends keyof IAttr>(param?: T){
    // 无参数 返回当前类所有属性
    if ( typeof param === 'undefined') {
      return this.attrs;
    }
    // 只有一个参数属性名 返回该属性值
    if (typeof param === 'string') {
      return this.attrs[param];
    }
  }

  setAttrs(name: Partial<IAttr>): void;
  setAttrs<T extends keyof IAttr>(name: T, value: IAttr[T]): void;
  setAttrs<T extends keyof IAttr>(param: T | Partial<IAttr>, value?: IAttr[T]) {
    // 设置属性值
    if (typeof param === 'string') {
      this.attrs[param] = value;
      return;
    }

    // 设置属性值
    if (typeof param === "object") {
      this.attrs = Object.assign({}, this.attrs, param);
      return;
    }
  }

  getParent(): IElement {
    return this.props.parent;
  }

  getContext() {
    return this.props.context;
  }

  // 获取图形边界
  getBoundry(): IBoundry {
    return {
      top: null,
      right: null,
      bottom: null,
      left: null
    }
  }

  saveContext() {
    this.props.context.save();
  }

  setContext(ctx: CanvasRenderingContext2D) {
    this.props.context = ctx;
  }

  restoreContext() {
    this.props.context.restore();
  }

  drawInner() { }

  createPath() { };
}